package com.github.lwhite1.tablesaw.util;

/**
 *
 */

import it.unimi.dsi.fastutil.ints.IntComparator;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

public class IntComparatorChain implements IntComparator, Serializable {

  private static final long serialVersionUID = -721644942746081630L;
  private final List<IntComparator> comparatorChain;
  private BitSet orderingBits;
  private boolean isLocked;

  public IntComparatorChain() {
    this(new ArrayList<>(), new BitSet());
  }

  public IntComparatorChain(IntComparator comparator) {
    this(comparator, false);
  }

  public IntComparatorChain(IntComparator comparator, boolean reverse) {
    this.orderingBits = null;
    this.isLocked = false;
    this.comparatorChain = new ArrayList<>(1);
    this.comparatorChain.add(comparator);
    this.orderingBits = new BitSet(1);
    if (reverse) {
      this.orderingBits.set(0);
    }

  }

  public IntComparatorChain(List<IntComparator> list) {
    this(list, new BitSet(list.size()));
  }

  public IntComparatorChain(List<IntComparator> list, BitSet bits) {
    this.orderingBits = null;
    this.isLocked = false;
    this.comparatorChain = list;
    this.orderingBits = bits;
  }

  public void addComparator(IntComparator comparator) {
    this.addComparator(comparator, false);
  }

  public void addComparator(IntComparator comparator, boolean reverse) {
    this.checkLocked();
    this.comparatorChain.add(comparator);
    if (reverse) {
      this.orderingBits.set(this.comparatorChain.size() - 1);
    }
  }

  public void setComparator(int index, IntComparator comparator) throws IndexOutOfBoundsException {
    this.setComparator(index, comparator, false);
  }

  public void setComparator(int index, IntComparator comparator, boolean reverse) {
    this.checkLocked();
    this.comparatorChain.set(index, comparator);
    if (reverse) {
      this.orderingBits.set(index);
    } else {
      this.orderingBits.clear(index);
    }
  }

  public void setForwardSort(int index) {
    this.checkLocked();
    this.orderingBits.clear(index);
  }

  public void setReverseSort(int index) {
    this.checkLocked();
    this.orderingBits.set(index);
  }

  public int size() {
    return this.comparatorChain.size();
  }

  public boolean isLocked() {
    return this.isLocked;
  }

  private void checkLocked() {
    if (this.isLocked) {
      throw new UnsupportedOperationException("Comparator ordering cannot be changed after the first comparison is " +
          "performed");
    }
  }

  private void checkChainIntegrity() {
    if (this.comparatorChain.size() == 0) {
      throw new UnsupportedOperationException("ComparatorChains must contain at least one Comparator");
    }
  }

  public int compare(Integer o1, Integer o2) throws UnsupportedOperationException {
    if (!this.isLocked) {
      this.checkChainIntegrity();
      this.isLocked = true;
    }

    Iterator comparators = this.comparatorChain.iterator();

    for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) {
      Comparator comparator = (Comparator) comparators.next();
      int retval = comparator.compare(o1, o2);
      if (retval != 0) {
        if (this.orderingBits.get(comparatorIndex)) {
          if (retval > 0) {
            retval = -1;
          } else {
            retval = 1;
          }
        }
        return retval;
      }
    }
    return 0;
  }

  public int compare(int o1, int o2) throws UnsupportedOperationException {
    if (!this.isLocked) {
      this.checkChainIntegrity();
      this.isLocked = true;
    }

    Iterator comparators = this.comparatorChain.iterator();

    for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) {
      IntComparator comparator = (IntComparator) comparators.next();
      int retval = comparator.compare(o1, o2);
      if (retval != 0) {
        if (this.orderingBits.get(comparatorIndex)) {
          if (retval > 0) {
            retval = -1;
          } else {
            retval = 1;
          }
        }
        return retval;
      }
    }
    return 0;
  }

  public int hashCode() {
    int hash = 0;
    if (null != this.comparatorChain) {
      hash ^= this.comparatorChain.hashCode();
    }

    if (null != this.orderingBits) {
      hash ^= this.orderingBits.hashCode();
    }
    return hash;
  }

  public boolean equals(Object object) {
    if (this == object) {
      return true;
    } else if (null == object) {
      return false;
    } else if (!object.getClass().equals(this.getClass())) {
      return false;
    } else {
      boolean var10000;
      label48:
      {
        label32:
        {
          IntComparatorChain chain = (IntComparatorChain) object;
          if (null == this.orderingBits) {
            if (null != chain.orderingBits) {
              break label32;
            }
          } else if (!this.orderingBits.equals(chain.orderingBits)) {
            break label32;
          }

          if (null == this.comparatorChain) {
            if (null == chain.comparatorChain) {
              break label48;
            }
          } else if (this.comparatorChain.equals(chain.comparatorChain)) {
            break label48;
          }
        }

        var10000 = false;
        return var10000;
      }

      var10000 = true;
      return var10000;
    }
  }
}
